/*
 * Systemprogrammierung
 * Multiplayer-Quiz
 *
 * Server
 *
 * clientthread.c: Implementierung des Client-Threads
 */

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <stdint.h>
#include "common/util.h"
#include "common/rfc.h"
#include "clientthread.h"
#include "score.h"
#include "user.h"
#include "login.h"


void clientt_CreateClientThread(int iSocket) {
	pthread_t oThread;

	infoPrint("\nCreating new ClientThread");
	pthread_create(&oThread, NULL, clientt_ClientThread, (void*) &iSocket);
	//

	return;
}

void *clientt_ClientThread(void* iSocket) {
	int iUserID;
	int iClientSocket;
	int iLen;
	int iFragmented = 0;
	int iTemp;
	char sTemp[512];
	char sBuffer[512];

	struct timespec sleepTime;
	struct timespec remainingSleepTime;

	iClientSocket = *((int*)iSocket);
	//sTemp = malloc(512 * sizeof(char));
	//sBuffer = malloc(512 * sizeof(char));

	sleepTime.tv_sec=1;
	sleepTime.tv_nsec=500000;


	infoPrint("\nWaiting on login data...");
	// Auf Daten warten
	bzero(sBuffer, 512);
	iUserID = server_GetMessage(iClientSocket, sBuffer);
	if (iUserID > 0) {
		sBuffer[iUserID] = '\0';
		infoPrint("\n%i bytes received", iUserID);
	    // Ueberpruefen auf Login-Request (1. Byte (int)1)
	    if ((int)sBuffer[0] == 1) {
	    	// Gueltiges Request
	        strcpy(sTemp, sBuffer + 3);
	        infoPrint("\nLogin: name: %s", sTemp);

	        // Pruefe Spielphase

	        // Pruefe Nutzeranzahl
	        if (user_GetUserCount() < 4) {
				// Pruefe Mindestlaenge
				if (strlen(sTemp) > 0) {
					// Pruefe Maximallaenge
					if (strlen(sTemp) < 32) {
						// Pruefe Einzigartigkeit des Namens
						if (user_UsernameAlreadyTaken(sTemp) == 0) {
							iUserID = user_AddNewUser(sTemp, iClientSocket);
							if (iUserID > -1) {
								// ID zu String konvertieren
								sBuffer[0] = 0;
								sBuffer[1] = 1;
								sBuffer[2] = (uint8_t)iUserID;
								// Antwort senden
								server_SendServerMessage(iClientSocket, server_CreateMessage(TYPE_IS_LOGIN_RESPONSE_OK, sBuffer, 3));
								sem_post(scoret_GetScoreSemaphore());
							}
							else {
								server_SendErrorMessage(iClientSocket, "Fehler: Konnte user nicht anlegen", 1);
								close(iClientSocket);
								pthread_exit(0);
							}
						}
						else {
							server_SendErrorMessage(iClientSocket, "Fehler: Name bereits in Verwendung", 1);
							close(iClientSocket);
							pthread_exit(0);
						}
					}
					else {
						server_SendErrorMessage(iClientSocket, "Fehler: Name zu lang", 1);
						close(iClientSocket);
						pthread_exit(0);
					}
				}
				else {
					server_SendErrorMessage(iClientSocket, "Fehler: Name zu kurz", 1);
					close(iClientSocket);
					pthread_exit(0);
				}
	        }
	        else {
	        	server_SendErrorMessage(iClientSocket, "Fehler: Alle Plaetze belegt", 1);
	        	close(iClientSocket);
	        	pthread_exit(0);
	        }
	   	}


	}
	else {
		errorPrint("\nERROR failed reading data");
		close(iClientSocket);
		pthread_exit(0);
	}

	while (1) {
		nanosleep(&sleepTime,&remainingSleepTime);

		iLen = server_GetMessage(iClientSocket, sBuffer);
		if (iLen > 0) {
			infoPrint("\nClient still connected, %d bytes received, [0] = %d", iLen, sBuffer[0]);
			//infoPrint("\nRead: %s", sBuffer);

			if (iFragmented == 1) {
				errorPrint("\nFragmentation continued ... recovering %d bytes", iTemp);
				memmove(sTemp + iTemp, sBuffer, iLen);
				memmove(sBuffer, sTemp, iTemp + iLen);
				iLen += iTemp;
			}


			if (((int)sBuffer[0] != 255) && ((int)sBuffer[0] > 12)) {
				// invalid package, trying -2
				errorPrint("\nInvalid Package, moving by -2");
				memmove(sBuffer, sBuffer + 2, iLen - 2);
				errorPrint("\nNew [0] = %d", sBuffer[0]);
			}

			iTemp = (int)sBuffer[1] * 256;
			iTemp += (int)sBuffer[2];

			if ((iLen - 3 != iTemp) && (iFragmented == 0)) {
				errorPrint("\nFragmentation detected! Securing %d bytes ...", iLen);
				iFragmented = 1;
				memmove(sTemp, sBuffer, iLen);
				iTemp = iLen;

			} else {
				iFragmented = 0;
				switch ((int)sBuffer[0]) {
					case 3:
						infoPrint("\nClient %d: Catalog Request received", iClientSocket);
						// Check phase
						if (login_GetSpielphase() == 0) {
							// Respond
							catalog_SendCatalogsToClient(iClientSocket);
						} else {
							// Send error
							server_SendErrorMessage(iClientSocket, "Fehler: Spielphase aktiv", 0);
						}
						break;
					case 5:
						infoPrint("\nClient %d: Catalog Change received", iClientSocket);
							// Check phase
							if (login_GetSpielphase() == 0) {
								memmove(sTemp, sBuffer + 3, iLen - 3);
								sTemp[iLen - 3] = '\0';
								//memmove(sTemp + iLen - 2, '\0', 1);

								infoPrint("Catalog read: %s", sTemp);

								// select catalog
								if (catalog_MarkCatalogAsSelected(sTemp) == 1) {
									// broadcast to all
									catalog_BroadcastSelectedCatalog();
								} else {
									errorPrint("\nError selecting Catalog: %s", sTemp);
								}
							} else {
								// Send error
								server_SendErrorMessage(iClientSocket, "Fehler: Spielphase aktiv", 0);
							}

						break;
					case 7:
						infoPrint("\nClient %d: Start Game received", iClientSocket);
						// Check phase
						if (user_GetUserCount() > 1) {
							if (login_GetSpielphase() == 0) {
								//loader

								iTemp = ((int)sBuffer[1] * 256) + (int)sBuffer[2];
								memmove(sTemp, sBuffer + 3, iTemp);
								sTemp[iTemp] = '\0';
								infoPrint("\nClient %d: Read catalog name: %s", iClientSocket, sTemp);

								if (loader_LoadCatalog(sTemp, iTemp) == 0) {
									infoPrint("\nClient %d: Loading Catalog succeeded!", iClientSocket);
								} else {
									infoPrint("\nClient %d: Loading Catalog failed!", iClientSocket);
								}

								// Reset question indices
								user_ResetAllQuestionIndices();
								// Broadcast start game
								scoret_BroadcastStartGame();
								// Set Spielphase
								login_SetSpielphase(1);
								sem_post(scoret_GetScoreSemaphore());

							} else {
								// Send error
								server_SendErrorMessage(iClientSocket, "Fehler: Spielphase aktiv", 0);
							}
						} else {
							server_SendErrorMessage(iClientSocket, "Fehler: nicht genug mitspieler", 0);
						}
						break;
					case 8:
						infoPrint("\nClient %d: Question request received", iClientSocket);
						// Check phase
						if (login_GetSpielphase() == 1) {
							// Send question
							loader_SendNextQuestion(iUserID);
						} else {
							// Send error
							server_SendErrorMessage(iClientSocket, "Fehler: Spielphase nicht aktiv", 0);
						}
						break;
					case 10:
						infoPrint("\nClient %d: Question answer received", iClientSocket);
						// Check phase
						if (login_GetSpielphase() == 1) {
						// Send question
							loader_checkAnswer(iUserID, (int)(sBuffer[3]));
						} else {
						// Send error
							server_SendErrorMessage(iClientSocket, "Fehler: Spielphase nicht aktiv", 0);
						}
						break;
					default:

						break;
				}
			}





		}
		else {
			// Socket is disconnected

			// todo: delete user
			// todo: broadcast user list
			infoPrint("\nClient disconneced: %i", iUserID);

			if ((user_GetUserCount() < 3) && (login_GetSpielphase() == 1)) {
				// zuwenig spieler
				user_DisconnectAllUsers();
			} else {
				// disconnect everyone if user was admin
				if ((iUserID == 0) && (login_GetSpielphase() == 0)) {
					user_DisconnectAllUsers();
				} else {
					if (user_DeleteUser(iUserID) > 0) {
						infoPrint("User successfully deleted: %i", iUserID);
					} else {
						infoPrint("Error deleting user: %i", iUserID);
					}
				}
			}
			close(iClientSocket);

			sem_post(scoret_GetScoreSemaphore());

			if (login_GetSpielphase() == 1) {
				loader_CheckGameEnd();
			}

			pthread_exit(0);
		}
	}

	pthread_exit(0);
}


